//
//  Rexel.c
//  Rexel
//
//  Created by Zach Zahos on 2014-05-18.
//  Copyright (c) 2014 Zach Zahos. All rights reserved.
//

#include <stdlib.h>
#include <string.h>
#include "Rexel.h"

const RXLType RXLTypeEOF = (RXLType)-1;
const RXLType RXLTypeUnknown = (RXLType)-2;

RXLTokenizer* RXLTokenizerCreate() {
	RXLTokenizer *tokenizer = malloc(sizeof(RXLTokenizer));
	tokenizer->typeCount = 0;
	tokenizer->maxSize = (1 << 3);
	tokenizer->typeDefs = malloc(sizeof(RXLTypeDef) * tokenizer->maxSize);
	tokenizer->settings = malloc(sizeof(void *) * tokenizer->maxSize);
	memset(tokenizer->typeDefs, 0, sizeof(RXLTypeDef) * tokenizer->maxSize);
	memset(tokenizer->settings, 0, sizeof(void *) * tokenizer->maxSize);
	return tokenizer;
}

RXLType RXLTokenizerAddTypeDef(RXLTokenizer *tokenizer, RXLTypeDef typeDef, void *context) {
	if (tokenizer->typeCount > tokenizer->maxSize) {
		RXLTypeDef *oldDefs = tokenizer->typeDefs;
		void **oldContexts = tokenizer->settings;
		unsigned int oldSize = tokenizer->maxSize;
		tokenizer->maxSize <<= 1;
		tokenizer->typeDefs = malloc(sizeof(RXLTypeDef) * tokenizer->maxSize);
		tokenizer->settings = malloc(sizeof(void *) * tokenizer->maxSize);
		memcpy(tokenizer->typeDefs, oldDefs, sizeof(RXLTypeDef) * oldSize);
		memset(tokenizer->typeDefs + tokenizer->typeCount, 0, sizeof(RXLTypeDef) * (tokenizer->maxSize - oldSize));
		memcpy(tokenizer->settings, oldContexts, sizeof(void *) * oldSize);
		memset(tokenizer->settings + tokenizer->typeCount, 0, sizeof(void *) * (tokenizer->maxSize - oldSize));
		free(oldDefs);
	}
	tokenizer->typeDefs[tokenizer->typeCount] = typeDef;
	tokenizer->settings[tokenizer->typeCount] = context;
	return tokenizer->typeCount++;
}

RXLTokenizeRequest *RXLTokenizeRequestCreateBuffer(char *buffer, unsigned long length) {
	RXLTokenizeRequest *request = malloc(sizeof(RXLTokenizeRequest));
	request->buffer = buffer;
	request->length = length;
	request->currentStart = request->currentOffset = 0;
	return request;
}

typedef struct {
	RXLType type;
	RXLValidity validity;
	void *context;
} RXLTypeValidity;

RXLToken* RXLTokenizerNextToken(RXLTokenizer *tokenizer, RXLTokenizeRequest *request) {
	RXLToken *token = malloc(sizeof(RXLToken));
	if (request->currentOffset >= request->length) {
		token->type = RXLTypeEOF;
		token->length = 0;
		token->token = NULL;
		return token;
	}
	int typeMatches = 0;
	RXLTypeValidity **matches = malloc(sizeof(RXLTypeValidity *) * tokenizer->typeCount);//[tokenizer->typeCount];
	for (RXLType t = 0; t < tokenizer->typeCount; t++) {
		RXLTypeDef typeDef = tokenizer->typeDefs[t];
		void *context = NULL;
		RXLValidity validity = typeDef(request->buffer+request->currentStart, 0, tokenizer->settings[t], &context);
		if (validity != RXLValidityInvalid) {
			matches[typeMatches] = malloc(sizeof(RXLTypeValidity));
			matches[typeMatches]->type = t;
			matches[typeMatches]->validity = validity;
			matches[typeMatches]->context = context;
			typeMatches++;
		}
	}
	request->currentOffset++;
	if (typeMatches == 0) {
		token->type = RXLTypeUnknown;
		token->length = 1;
		token->token = malloc(sizeof(char));
		token->token[0] = *(request->buffer+request->currentStart);
	} else {
		int lastActive = typeMatches-1;
		while (lastActive >= 0 && request->currentOffset < request->length) {
			int found = 0;
			for (int i = 0; i <= lastActive;) {
				RXLTypeValidity *typeValidity = matches[i];
				RXLTypeDef typeDef = tokenizer->typeDefs[typeValidity->type];
				RXLValidity lastValidity = typeValidity->validity;
				typeValidity->validity = typeDef(request->buffer+request->currentStart, request->currentOffset-request->currentStart, tokenizer->settings[typeValidity->type], &typeValidity->context);
				if (typeValidity->validity == RXLValidityValid || typeValidity->validity == RXLValidityIncomplete) {
					found++;
					i++;
				} else {
					if (typeValidity->validity == RXLValidityInvalid) {
						for (int j = i; j < typeMatches-1; j++) {
							matches[j] = matches[j+1];
						}
						typeMatches--;
					} else {
						matches[i] = matches[lastActive];
						matches[lastActive] = typeValidity;
						if (lastValidity == RXLValidityIncomplete && typeValidity->validity == RXLValidityComplete) {
							found++;
							i++;
						}
					}
					lastActive--;
				}
			}
			if (!found) {
				break;
			}
			request->currentOffset++;
		}
		if (!typeMatches || (request->currentOffset >= request->length && matches[0]->validity == RXLValidityIncomplete)) {
			request->currentOffset = request->currentStart + 1;
			token->type = RXLTypeUnknown;
		} else {
			token->type = matches[0]->type;
		}
		token->length = request->currentOffset - request->currentStart;
		token->token = malloc(sizeof(char) * (token->length + 1));
		token->token[token->length] = 0;
		memcpy(token->token, request->buffer + request->currentStart, token->length);
	}
	request->currentStart = request->currentOffset;
	return token;
}
